import uuid
from datetime import datetime, timedelta
from typing import Optional, Annotated
import json

import jwt
from fastapi import Depends
from fastapi.security import OAuth2PasswordBearer
from jwt import InvalidTokenError
from pydantic import BaseModel
from starlette.requests import Request

from src.base.context import web_login_user
from src.base.enums import DataScopeType
from src.base.exception import AuthException
from src.config.db import redisClient
from src.dao.sys_dept_dao import get_dept_and_child_list
from src.dao.sys_role_dao import get_role_list_by_user_id

JWT_SECRET = '123456'
JWT_ALGORITHM = 'HS512'
JWT_EXPIRATION = 86400
JWT_TOKEN_PREFIX = 'Bearer'
JWT_CACHE_KEY_PREFIX = 'fast-test:login_user:'
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/login")


class DataScope(BaseModel):
    dept_ids: Optional[list[int]] = None
    user_ids: Optional[list[int]] = None


class LoginUser(BaseModel):
    user_id: Optional[int] = None
    user_name: Optional[str] = None
    nick_name: Optional[str] = None
    tenant_id: Optional[int] = None
    dept_id: Optional[int] = None
    data_scope: Optional[DataScope] = DataScope()
    last_login_time: Optional[datetime] = None
    expire_time: Optional[float] = None
    user_type: Optional[str] = None
    login_ip: Optional[str] = None
    cache_key: Optional[str] = None


def get_cache_key(cache_key: str) -> str:
    return f'{JWT_CACHE_KEY_PREFIX}{cache_key}'


# 生成token
def create_access_token(login_user: LoginUser) -> str:
    user_ids, dept_ids = set_data_scope(login_user.user_id, login_user.dept_id)
    login_user.data_scope.user_ids = user_ids
    login_user.data_scope.dept_ids = dept_ids
    login_user.cache_key = str(uuid.uuid4())
    login_user.expire_time = JWT_EXPIRATION
    redisClient.set(get_cache_key(login_user.cache_key), login_user.model_dump_json(), login_user.expire_time)
    return jwt.encode({'id': login_user.cache_key}, JWT_SECRET, algorithm=JWT_ALGORITHM)


# 校验token
def verify_access_token(token: str) -> LoginUser:
    try:
        payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
        if payload is None:
            raise AuthException(name='Auth Error', message='token 不合法')
        if payload['id'] is None:
            raise AuthException(name='Auth Error', message='token 不合法')
        cache_key = payload['id']
        login_user = redisClient.get(get_cache_key(cache_key))

        if login_user is None:
            raise AuthException(name='Auth Error', message='token已过期')
    except InvalidTokenError:
        raise AuthException(name='Auth Error', message='token 不合法')
    return LoginUser.model_validate(json.loads(login_user))


# 认证依赖
async def auth_depend(request: Request, token: Annotated[str, Depends(oauth2_scheme)]):
    login_user = verify_access_token(token)
    request.state.login_user = login_user
    web_login_user.set(login_user)


def set_data_scope(user_id, dept_id):
    user_ids = None
    dept_ids = None
    roles = get_role_list_by_user_id(user_id)
    data_scope_list = [role.data_scope for role in roles]
    if DataScopeType.ALL.value not in data_scope_list:
        user_ids = list()
        dept_ids = list()
        for role in roles:
            if role.data_scope == DataScopeType.CUSTOM.value:
                dept_ids.extend(role.custom_dept.split(','))
                user_ids.extend(role.custom_user.split(','))
            elif role.data_scope == DataScopeType.DEPT_AND_CHILD.value:
                dept_list = get_dept_and_child_list([dept_id])
                dept_ids.extend([dept.dept_id for dept in dept_list])
            elif role.data_scope == DataScopeType.DEPT_ONLY.value:
                dept_ids.append(dept_id)
            elif role.data_scope == DataScopeType.SELF.value:
                user_ids.append(user_id)
    return user_ids, dept_ids
